/*
   Establece un manejador externo para controlar el teclado, recoge los parametros de la operacion
   por este y envia la peticion al servidor que devolvera el resultado de aplicar la operacion
   sobre los parametros recogidos.

   El cliente de esta aplicacion es: http_server.p => Transporte: http::Transport.
*/
#include <iostream>

#include <string.h>

#include <nemesis.h>
#include <nemesis.comm.h>

#include <nemesis.http.Request.h>
#include <nemesis.http.Response.h>
#include <nemesis.http.Handler.h>
#include <nemesis.http.Transport.h>
#include <nemesis.http.functions.h>

#include <test.Menu.h>
#include <test.Request.h>
#include <test.Response.h>

class MyHandler : public http::Handler {
public:
   MyHandler () : http::Handler ("http_kclient::MyHandler") {;}

private:
   http::Response a_httpResponse;
   test::Response a_testResponse;

   void evRequest (ClientSocket&, const http::Request&) throw (RuntimeException) {;}
   void evResponse (ClientSocket&, const http::Response&) throw (RuntimeException);
};

class MyCommunicator : public Communicator {
public:
   MyCommunicator () : Communicator ()
   {
      a_httpRequest.setMethod (http::Method::Type::Post);
      a_httpRequest.setURI ("HTTPKClient");
   }

private:
   MyHandler a_httpHandler;
   http::Request a_httpRequest;
   test::Request a_testRequest;

   void eventReceiveMessage (ClientSocket &, const Message&) throw (RuntimeException);
   void eventUser (const char* id, const void* context) throw ();
};

class HTTPKClient : public nemesis::comm::Application {
public:
   HTTPKClient ();

   Server* getServer () const throw () { return a_server; }
   const test::Menu& getMenu () const throw () { return a_menu; }

private:
   MyCommunicator a_communicator;
   test::Menu a_menu;
   Server* a_server;

   void initialize () throw (RuntimeException);
   void run () throw (RuntimeException);
};

using namespace std;
using namespace test;

int main (int argc, const char** argv)
{
   CommandLine& commandLine (CommandLine::instanciate ());
   HTTPKClient app;

   http::functions::initialize ();

   try {
      commandLine.initialize (argv, argc);
      commandLine.verify ();

      Logger::setLevel (Logger::Debug);
      Logger::initialize ("http_kclient", new TraceWriter ("file.trace", 4096000));

      app.start ();
   }
   catch (Exception& ex) {
      cout << ex.asString () << endl;
   }

   return 0;
}

HTTPKClient::HTTPKClient () :
   Application ("kclient", "HTTPKClient", "1.0.0"),
   a_menu (&a_communicator)
{
   CommandLine& commandLine (CommandLine::instanciate ());

   commandLine.add ("p", CommandLine::Argument::Mandatory, "Puerto en el que el servidor atiende respuestas.");
   commandLine.add ("a", CommandLine::Argument::Mandatory, "Direccin IP Puerto en el que el servidor atiende respuestas.");
}

void HTTPKClient::initialize () 
   throw (RuntimeException)
{
   CommandLine& cl (CommandLine::instanciate ());

   Network& network = Network::instanciate ();

   Host* host = network.find ("host000");
   host->assign (network.find (Device::asAddress (cl.getValue ("a"))));
   a_server = host->createServer ("http_server", cl.getIntegerValue ("p"), true, &http::Transport::getFactory ());

   a_communicator.attach (&a_menu);
}

void HTTPKClient::run ()
   throw (RuntimeException)
{
   a_menu.paint ();
   a_communicator.accept ();
}

void MyCommunicator::eventReceiveMessage (ClientSocket& clientSocket, const Message& message)
   throw (RuntimeException)
{
   LOGMETHOD (TraceMethod tm ("MyCommunicator", "eventReceiveMessage", FILE_LOCATION));

   if (clientSocket.support (http::Transport::className ()) == false)
      return;

   a_httpHandler.apply (clientSocket, message);
}

//-----------------------------------------------------------------------------------------
// Cuando el Menu tiene disponibles todos los datos necesarios para la peticin se lo
// notifica al comunicador mediante un evento de usuario.
//-----------------------------------------------------------------------------------------
void MyCommunicator::eventUser (const char* id, const void* context)
   throw ()
{
   LOGMETHOD (TraceMethod tm ("MyCommunicator", "eventUser", FILE_LOCATION));

   if (nemesis_strcmp (id, Menu::EventData) == 0) {
      const Menu::Data* data (reinterpret_cast <const Menu::Data*>  (context));

      a_testRequest.op = data->a_operation;
      a_testRequest.x = data->a_op1;
      a_testRequest.y = data->a_op2;
      a_testRequest.initTime = nemesis::functions::milisecond ();

      Server* server = static_cast <HTTPKClient&> (nemesis::comm::functions::getApp ()).getServer ();

      try {
         a_httpRequest.setBody (a_testRequest.code ());
         server->send (a_httpRequest);
      }
      catch (RuntimeException& ex) {
         ex.trace ();
      }
   } 
}

void MyHandler::evResponse (ClientSocket& clientSocket, const http::Response& response)
   throw (RuntimeException)
{
   if (response.getStatusCode () == 200) {
      a_testResponse.decode (response.getBody ());
      
      const Milisecond responseTime = nemesis::functions::milisecond () - a_testResponse.initTime;  
      cout << endl << "ResponseTime: " << responseTime << " ms" << endl;
      cout << endl << "Resultado de la peticion: " << a_testResponse.x << (char) a_testResponse.op << a_testResponse.y  << " = " << a_testResponse.result << endl << endl;
   }
   else 
      cout << endl << "Error en la peticion: " << response.getStatusCode () << " (" << response.getReasonPhrase () << ")" << endl << endl;
   
   static_cast <HTTPKClient&> (nemesis::comm::functions::getApp ()).getMenu ().paint ();
}

